/*
Copyright (c) 2013 Aaron Mark Brown http://aaronmarkbrown.com

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

package haxeframe;

import flash.utils.ByteArray;
import haxe.Json;
import haxe.macro.Expr.FieldType;
import haxe.Timer;
import haxeframe.events.LoadEvent;
import haxeframe.Globals;
import haxeframe.loading.LoadManager;

import haxeframe.loading.LoadObject;
import haxeframe.utils.Utils;
import haxeframe.sound.SoundManager;
import haxeframe.views.View;
import haxeframe.views.ViewManager;
import haxeframe.views.ViewProgress;

import flash.display.Sprite;
import flash.Lib;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.filesystem.File;
import flash.media.Sound;
import flash.net.URLLoader;
import flash.system.Capabilities;
import flash.ui.Multitouch;

class HaxeFrame extends View
{	
	public var configURL:String='xml/config.xml';
	
	private var stringsURL:String;
	
	public var online:Bool = true;
	public var debug:Bool = false;
	
	private var onCompleteState:String;
	
	public var baseMediaURL:String;
	public var baseDataURL:String;

	private var stateConfig:Map<String, Xml>;
	private var viewConfig:Map<String, Xml>;

	private var loadingState:String;

	private var progressIndicator:ViewProgress;

	private var _progressiveLoad:Bool = true;

	public function new()
	{
 		super ();
	}

	private function detectSize():Void
	{
		var w:Float = stage.stageWidth;
		var h:Float = stage.stageHeight;

		var screenWidth:Float = (w < h) ? h : w;
		var screenHeight:Float = (w > h) ? h : w;

		#if html5
			if(Globals.MOBILE){
				screenWidth = w;
				screenHeight = h;

				if(screenWidth == 480 && screenHeight == 356)
					screenHeight = 320;
			}
		#end

		Reflect.setProperty(_globals, 'ASPECT_RATIO', screenWidth/screenHeight);
		
		if(screenHeight != 320 && screenHeight != 768){
			Reflect.setField(_globals, 'CONTENT_DEFINITION', 'HD');
		}

		Reflect.setProperty(_globals, 'CONTENT_WIDTH', screenWidth);
		Reflect.setProperty(_globals, 'CONTENT_HEIGHT', screenHeight);
		Reflect.setProperty(_globals, 'CONTENT_SCALE', screenHeight/320);
		Reflect.setProperty(_globals, 'ASPECT_RATIO', screenWidth/screenHeight);
	}

	public override function init():Void
	{
 		#if cpp
	 		online = LoadManager.online = false;
	 		LoadManager.basePath = Std.string(File.applicationDirectory.nativePath)+'/assets/';
 		#end

 		LoadManager.scope = this;
		LoadManager.progressHandler = handleLoadProgress;
		LoadManager.completeHandler = onFileComplete;

 		if(Multitouch.supportsTouchEvents){
 			Globals.MOBILE_SUFFIX = '-mobile';
 			Globals.MOBILE = true;
 			SoundManager.useTouches(stage);
 		}

		super.init();		

		progressIndicator = new ViewProgress();

		stateConfig = new Map<String, Xml>();
		viewConfig = new Map<String, Xml>();

		stage.align = StageAlign.TOP_LEFT;
		stage.scaleMode = StageScaleMode.NO_SCALE;
		
		detectSize();
		
		ViewManager.onShow = onShow;
		ViewManager.beforeShow = beforeShow;
		
		ViewManager.onHide = onHide;
		ViewManager.beforeHide = beforeHide;

		ViewManager.scope = this;

		loadConfig();
	}

	private function loadConfig():Void
	{
		LoadManager.loadFile('config', configURL, null, null, onConfigLoad, this);
	}

	private override function onResize(event:Event):Void
	{
		detectSize();
	}

	public var progressiveLoad(get,set):Bool;
	public function get_progressiveLoad():Bool
	{
		return _progressiveLoad;
	}

	public function set_progressiveLoad(value:Bool):Bool
	{
		LoadManager.progressiveLoad = _progressiveLoad = value;
		return _progressiveLoad;
	}


	private function onStringsLoad(loadObject:LoadObject):Void
	{
		strings = Xml.parse(loadObject.file.toString()).firstElement();
	}

	private function onConfigLoad(loadObject:LoadObject):Void
	{
		if(LoadManager.getFile('config').nodeType == 'document'){
			config = LoadManager.getFile('config').firstElement();
		} else if(LoadManager.getFile('config').nodeType == 'element'){
			config = LoadManager.getFile('config');
		}
		if(config != null)
			handleConfig();
	}

	private function handleConfig():Void
	{
		var c:Class<Dynamic> = Type.getClass(this);
		var instanceFields:Array<String> = Type.getInstanceFields(c);
		var globalFields:Array<String> = Type.getClassFields(_globals);

		for(element in config.elements())
		{
			if(Utils.valueInArray(element.nodeName, instanceFields) || Utils.valueInArray('set_'+element.nodeName, instanceFields)){
				try {
					Reflect.setProperty(this, element.nodeName, element.firstChild());
				} catch(e:Dynamic) {
					trace(element.nodeName+' : '+e);
				}
			} else if(Utils.valueInArray(element.nodeName, globalFields) || Utils.valueInArray('set_'+element.nodeName, globalFields)){
				try {
					Reflect.setProperty(_globals, element.nodeName, element.firstChild());
				} catch(e:Dynamic) {
					trace(element.nodeName+' : '+e);
				}
			}
			if(element.nodeName == 'state'){
				var stateName:String = element.get('name');
				stateConfig[stateName] = element;
				var views:Array<String> = [];

				for(view in element.elementsNamed('view')){
					var viewName:String = view.get('name');
					if(view.exists('config')){
						LoadManager.addFile(viewName, view.get('config'), 'preload', null, onViewConfigLoad, this, stateName);
					}
					ViewManager.addView(viewName, Type.resolveClass(view.get('className')));
					views.push(view.get('name'));
				}
				ViewManager.addState(stateName, views);
			} else if(element.nodeName == 'strings'){
				stringsURL = element.get('file');
			} else if(element.exists('file')){
				var fileName:String = (element.exists('name')) ? element.get('name') : element.get('file');
				LoadManager.addFile(fileName, element.get('file'), 'preload', element.get('channel'), onFileComplete, this);
			}
		}

		if(stringsURL != null)
			LoadManager.loadFile('strings', stringsURL, null, null, onStringsLoad, this);

		addChild(progressIndicator);
		progressIndicator.show();
		progressIndicator.update(0);

		LoadManager.addEventListener(LoadEvent.QUEUE_COMPLETE, onPreloadComplete);
		LoadManager.addEventListener(LoadEvent.QUEUE_COMPLETE, onQueueComplete);
		if(progressiveLoad){
			LoadManager.currentQueue = 'preload';
			LoadManager.loadFromQueue();
		} else {
			LoadManager.startQueues();
		}
	}

	private function onViewConfigLoad(loadObject:LoadObject):Void
	{
		var thisViewConfig:Xml = Xml.parse(LoadManager.getFile(loadObject.name).toString()).firstElement();
		viewConfig.set(loadObject.name, thisViewConfig);
	}

	private function handleLoadProgress(progress:Float):Void
	{
		progressIndicator.update(progress);
	}

	private function onPreloadComplete(event:LoadEvent):Void
	{
		removeChild(progressIndicator);
		LoadManager.removeEventListener(LoadEvent.QUEUE_COMPLETE, onPreloadComplete);
	}

	private function onLoadComplete():Void
	{
		// Not implemented at this level
	}

	private function onFileComplete(loadObject:LoadObject):Void
	{
		if(Std.is(loadObject.file, Sound)){
			SoundManager.addSound(loadObject.name, loadObject.file);
		}
	}

	private function onJSoundComplete(params:Array<Dynamic>):Void
	{
		SoundManager.addSound(params[0], params[1], params[2]);
	}

	private function onQueueComplete(event:LoadEvent):Void
	{
		if(event.queue == loadingState){
			removeChild(progressIndicator);
			loadingState = '';
			ViewManager.showState(event.queue);
		}
		if(LoadManager._queueQueue.length == 0){
			onLoadComplete();
		}
	}
	
	private function initState(stateName:String):Bool
	{
		var stateXml:Xml = stateConfig.get(stateName);

		if(stateXml != null){

			for(element in stateXml.elements()){

				if(element.nodeName == 'view')
				{
					var viewName:String = element.get('name');
					var thisView:View = cast(ViewManager.getViewInstance(viewName), View);
					if(strings != null) thisView.strings = getViewStrings(stateName, viewName);
					thisView.config = viewConfig.get(viewName);
				}
			}

		} else {

			for(viewName in ViewManager.states[stateName]){
				var thisView:View = cast(ViewManager.getViewInstance(viewName), View);
				if(strings != null) thisView.strings = getViewStrings(stateName, viewName);
				thisView.config = viewConfig.get(viewName);
			}
			
		}

		return true;
	}

	private function getViewStrings(stateName:String, viewName:String):Xml
	{
		if(stateName != null){
			for(state in strings.elementsNamed('state')){
				if(state.get('name') == stateName)
				{
					for(view in state.elementsNamed('view'))
					{
						if(view.get('name') == viewName)
						{
							return view;
						}
					}
				}
			}
		}
		for(view in strings.elementsNamed('view')){
			if(view.get('name') == viewName)
			{
				return view;
			}
		}
		return null;
	}

	private function beforeShow(stateName:String):Bool
	{
		for(fileArray in LoadManager.getQueue(stateName)){
			var thisFile:Dynamic = LoadManager.getFile(fileArray[0]);
			if(thisFile == null){
				loadingState = stateName;
				addChild(progressIndicator);
				progressIndicator.show();
				progressIndicator.update(0);
				if(LoadManager.currentQueue != stateName && !_progressiveLoad){
					LoadManager.startQueue(stateName);
				}
				return false;
			}
		}

		return initState(stateName);
	}
	
	private function onShow(stateName:String):Void
	{

	}
	
	private function onHide(stateName:String):Void
	{
		if(!_progressiveLoad){
			LoadManager.clear();
		}
	}
	
	private function beforeHide(stateName:String):Bool
	{
		return true;
	}

	private override function renderLayout():Void {}

}